package com.czzhan.captcha.common.aspectj;

import com.alibaba.fastjson2.JSONObject;
import com.czzhan.captcha.common.annotation.WebInterceptCaptcha;
import com.czzhan.captcha.common.constant.Constant;
import com.czzhan.captcha.common.core.RedisCache;
import com.czzhan.captcha.common.dto.CaptchaDto;
import com.czzhan.captcha.common.dto.Result;
import com.czzhan.captcha.common.util.IpUtils;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * WebInterceptCaptchaAspect
 *
 * @author czzhan
 * @date 2024/5/5 15:39
 */
@Slf4j
@Aspect
@Order(1)
@Component
public class WebInterceptCaptchaAspect {

    @Resource
    private RedisCache redisCache;

    @Pointcut("@annotation(com.czzhan.captcha.common.annotation.WebInterceptCaptcha)")
    public void captchaPointCut() {
    }

    /**
     * 处理请求前执行
     */
    @Around("captchaPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        WebInterceptCaptcha  captchaPointCut = signature.getMethod().getAnnotation(WebInterceptCaptcha.class);
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        String ip = IpUtils.getIpAddr(request);
        String servletPath = request.getServletPath();
        CaptchaDto dto = new CaptchaDto(ip, servletPath);
        String hickey = Constant.REQ_CAPTCHA_HIS + dto.getKey();
        String str = redisCache.getCacheObject(hickey);
        if (StringUtils.isNotBlank(str)) {
            dto = JSONObject.parseObject(str, CaptchaDto.class);
        }
        // 如果已经达到当日最大调用次数
        if (captchaPointCut.ipUpperLimit() > 0 && captchaPointCut.ipUpperLimit() <= dto.getCount()) {
            return JSONObject.toJSONString(Result.failed("今日调用次数已达上限"));
        }
        // 如果达到验证码发送频率
        if (captchaPointCut.ipTriggerLimit() <= dto.getCount()) {
            // 获取请求参数，计算key值
            return captcha(point, request, dto);
        }
        return point.proceed();
    }


    @AfterReturning(returning = "object", pointcut = "captchaPointCut()")
    public void doAfterReturning(Object object) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = IpUtils.getIpAddr(request);
        String servletPath = request.getServletPath();
        CaptchaDto dto = new CaptchaDto(ip, servletPath);
        String hickey = Constant.REQ_CAPTCHA_HIS + dto.getKey();
        String str = redisCache.getCacheObject(hickey);
        if (StringUtils.isNotBlank(str)) {
            dto = JSONObject.parseObject(str, CaptchaDto.class);
            dto.setCount(dto.getCount() + 1);
        } else {
            dto.setCount(1);
        }
        redisCache.setCacheObject(hickey, JSONObject.toJSONString(dto), 1, TimeUnit.DAYS);
    }

    /**
     * captcha
     *
     * @author czzhan
     * @since 2024/5/5 19:51
     * @param point ProceedingJoinPoint
     * @param request HttpServletRequest
     * @param dto CaptchaDto
     * @return Object
    */
    private Object captcha(ProceedingJoinPoint point, HttpServletRequest request, CaptchaDto dto) throws Throwable {
        String key = Constant.REQ_CAPTCHA_KEY + dto.getKey();
        String code = request.getHeader(Constant.REQ_HEAD_CODE);
        String tmpCode = redisCache.getCacheObject(key);
        // 验证码验证通过场景
        if (StringUtils.isNotBlank(tmpCode) && StringUtils.equals(code, tmpCode)) {
            redisCache.deleteObject(key);
            return point.proceed();
        }
        // 没有验证码或验证不通过，重新保存验证码结果
        Captcha specCaptcha = easyGenerateCode();
        String content = specCaptcha.text();
        log.info("content={}", content);
        redisCache.setCacheObject(key, content, 300, TimeUnit.SECONDS);
        return Result.captcha(specCaptcha.toBase64());
    }

    /**
    * easyGenerateCode
    * 生成一个随机类型的验证码
    *
    * @author czzhan
    * @since 2024/5/5 17:26
    * @return Captcha
    */
    private Captcha easyGenerateCode() {
        Captcha specCaptcha;
        Random random = new Random();
        int n = random.nextInt(2);
        if (n == 0) {
            // 英文验证码
            specCaptcha = new SpecCaptcha();
            //设置5个字符
            specCaptcha.setLen(5);
        } else {
            // 数字计算验证码
            specCaptcha = new ArithmeticCaptcha();
            //设置2位数
            specCaptcha.setLen(2);
        }
        return specCaptcha;
    }

}
